package lv.clientedisqueria.model;

import java.util.List;

import javax.swing.DefaultListSelectionModel;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import lv.mensajeriadisqueria.idomain.IDomain;

/**
 * Esta es una tabla generica que maneja una la cantidad de columnas como asi tambien la 
 * cantidad de datos a mostrar.
 * Ademas posee un listener de cambios, si se agregan nuevos valores, se quitan o se seleccionan.
 * posee metodos abstractos que muestran mensajes dependiendo de la cual la herede.  
 *
 * @param <Entity> entidad a la cual se va a mostrar por tabla
 */
@SuppressWarnings("serial")
public abstract class DisqueriaTablaModelo<Entity extends IDomain> extends AbstractTableModel {

	private ListSelectionModel listSelectionModel;
	private String[] columns;
	private List<Entity> domains;
	private Entity seleccionado;

	/**
	 * llama al metodo de cada modelo que la invoque para buscar todos los datos de las clase
	 * entities.
	 * @param modelo
	 * @param columns columnas que va a mostrar
	 * @throws Exception 
	 */
	public DisqueriaTablaModelo(Modelo<Entity> modelo, String[] columns) {
		this.columns = columns;
		domains = modelo.buscarTodos();
		cambiarValor(modelo);
	}

	/**
	 * tiene un listener que permite escuchar cuando la seleccion cambio.
	 * Le informa a la vista cual es la entidad que cambio de seleccion
	 * @param modelo
	 */
	private void cambiarValor(final Modelo<Entity> modelo) {
		listSelectionModel = new DefaultListSelectionModel();
		listSelectionModel.addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent e) {
				int index = 0;
				if (listSelectionModel.isSelectedIndex(e.getFirstIndex())) {
					index = e.getFirstIndex();
				} else if (listSelectionModel.isSelectedIndex(e.getLastIndex())) {
					index = e.getLastIndex();
				}
				if (!domains.isEmpty()) {
					Entity entity = domains.get(index);
					setSeleccionado(entity);
					modelo.firePropertyChange(modelo.getPropertyChangeCambio(), 
							null, entity);
				}
			}
		});
		listSelectionModel.setSelectionInterval(0, 0);
	}

	public Entity getSeleccionado() {
		return seleccionado;
	}

	public void setSeleccionado(Entity seleccionado) {
		this.seleccionado = seleccionado;
	}

	public List<Entity> getDomains() {
		return domains;
	}

	public ListSelectionModel getListSelectionModel() {
		return listSelectionModel;
	}

	/**
	 * 
	 */
	public abstract void actualizarTabla();
	
	/**
	 * remueve la entidad que esta seleccionada
	 */
	public void remover() {
		int indexOf = domains.indexOf(seleccionado);
		int newIndex = indexOf-1;
		domains.remove(indexOf);
		if (domains.isEmpty()) {
			seleccionado = null;
		} else {
			if(newIndex < 0){
				newIndex = 0;
			}
			seleccionado = domains.get(newIndex);
			listSelectionModel.setSelectionInterval(newIndex, newIndex);
		}
	}

	/**
	 * adhiere una entidad a la seleccion o la modifica dependiendo de su existencia o no
	 * Luego la deja seleccionada.
	 * @param entity
	 */
	public void adherirOModificar(Entity entity) {
		if(domains.contains(entity)) {
			modificarEntity(domains.get(domains.indexOf(entity)), entity);
		} else {
			domains.add(entity);
		}
		ordenar(domains);
		int index = domains.indexOf(entity);
		setSeleccionado(domains.get(index));
		listSelectionModel.setSelectionInterval(index, index);
	}

	/**
	 * Selecciona el primero de la lista 
	 */
	public void seleccionaPrimero() {
		getListSelectionModel().clearSelection();
		getListSelectionModel().setSelectionInterval(0, 0);
	}

	/**
	 * Ordena la lista de menor a mayor
	 * @param lista
	 */
	protected abstract void ordenar(List<Entity> lista);

	/**
	 * devuelve los valores de la lista recorriendo fila por columna.
	 * @param rowIndex
	 * @param columnIndex
	 * @return
	 */
	protected abstract Object valor(int rowIndex, int columnIndex);
	
	/**
	 * modifica los parametros de una entidad existente por los de una nueva.
	 * @param oldEntity
	 * @param newEntity
	 */
	protected abstract void modificarEntity(Entity oldEntity, Entity newEntity);

//************************************* MENSAJES DE ABSTRACT TABLE MODEL *******************************************
	@Override
	public int getColumnCount() {
		return columns.length;
	}

	@Override
	public int getRowCount() {
		return domains.size();
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		return valor(rowIndex, columnIndex);
	}

	@Override
	public String getColumnName(int column) {
		return columns[column];
	}
}